package com.brillilab.starter.controller.android.user;

import com.alibaba.fastjson.JSONObject;
import com.brillilab.common.constant.ClientTypeEnum;
import com.brillilab.common.constant.KeyProfixConstant;
import com.brillilab.common.constant.ResultEnum;
import com.brillilab.common.entity.ResponseVo;
import com.brillilab.common.exceptions.BrillilabException;
import com.brillilab.common.redis.RedisCache;
import com.brillilab.common.utils.CodeUtil;
import com.brillilab.common.utils.ValidatorUtil;
import com.brillilab.domain.po.lab.LabDevice;
import com.brillilab.domain.po.lab.LabMember;
import com.brillilab.domain.po.user.Users;
import com.brillilab.domain.vo.lab.LabInfoVo;
import com.brillilab.domain.vo.user.*;
import com.brillilab.service.core.lab.ILabDeviceService;
import com.brillilab.service.core.lab.ILabMemberService;
import com.brillilab.service.core.user.IUsersService;
import com.brillilab.service.logic.message.MessageLogic;
import com.brillilab.service.logic.user.UserInviteAuditLogic;
import com.brillilab.service.logic.user.UsersLogic;
import com.brillilab.starter.aspect.AspectLog;
import com.brillilab.starter.constant.StarterConstant;
import com.brillilab.starter.entity.req.user.UserModifyReq;
import com.brillilab.starter.entity.req.user.UserRegisterReq;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.List;


/**
 * 用户API
 *
 * @author wmh
 */
@RestController("AndroidUserController")
@RequestMapping("/android/user")
public class UserController {

    @Autowired
    private IUsersService usersService;
    @Autowired
    private UsersLogic usersLogic;
    @Autowired
    private ILabDeviceService labDeviceService;
    @Autowired
    private MessageLogic messageLogic;
    @Autowired
    private RedisCache redisCache;
    @Autowired
    private ILabMemberService labMemberService;

    @Resource
    private UserInviteAuditLogic userInviteAuditLogic;

    /**
     * 账号登录 +
     * {\"labId\":\"实验室ID 选填(一体机登录) L\",\"mobile\":\"用户手机号 必填 S\",\"password\":\"用户密码 必填 S\"}
     *
     * @return UserInfoVo (加入token字段,labId需要返回)
     */
    @AspectLog
    @PostMapping("/accountLogin")
    public ResponseVo userAccountLogin(@RequestBody UserLogin users) {

        Long labId = users.getLabId();
        String mobile = users.getMobile();
        String password = users.getPassword();

        Assert.isTrue(!StringUtils.isBlank(mobile), "请输入手机号！");
        Assert.isTrue(ValidatorUtil.isPhone(mobile), "手机号格式错误！");
        Assert.isTrue(!StringUtils.isBlank(password), "请输入密码！");

        UserInfoVo userInfoVo = usersLogic.userLogin(labId, mobile, password, ClientTypeEnum.ANDROID);

        return ResponseVo.success(userInfoVo);
    }


    /**
     * 二维码登录 +
     * <p>
     * deviceNo 设备编号
     * qrCode   1.用户实验室ID与labId是否相同
     * 2.qrCode绑定用户信息存入缓存
     *
     * @return
     */
    @GetMapping("/qrLogin")
    public ResponseVo userQRcodeLogin(String deviceNo, String qrCode, @RequestHeader(name = StarterConstant.TOKEN) String token) {

        if (StringUtils.isBlank(token)) {
            return ResponseVo.failure(ResultEnum.FAILURE, "token缺失！", null);
        }
        if (StringUtils.isBlank(deviceNo) || StringUtils.isBlank(qrCode)) {
            return ResponseVo.failure(ResultEnum.REQUEST_PARAM_LACK);
        }
        LabDevice labDevice = labDeviceService.selectByNo(deviceNo);
        if (labDevice.getLabId() == null) {
            return ResponseVo.failure(ResultEnum.REQUEST_PARAM_ERROR);
        }
        //获取缓存信息
        UserInfoVo userInfo = usersLogic.getUserByToken(token);

        //用户实验室ID与缓存用户信息labId是否相同，不同则不允许登录
        Assert.isTrue(userInfo.getLabId().equals(labDevice.getLabId()), "用户未切换到该实验室！");

        //qrCode绑定用户信息存入缓存
        redisCache.set(KeyProfixConstant.TOKEN + qrCode, userInfo, 60 * 30);

        return ResponseVo.success(userInfo);
    }

    /**
     * 二维码登录轮询
     * <p>
     * 根据qrCode从缓存取信息
     *
     * @return UserInfoVo (加入token字段,labId需要返回)
     */
    @GetMapping("/checkQrLogin")
    public ResponseVo checkQRLogin(String qrCode) {


        if (StringUtils.isBlank(qrCode)) {
            return ResponseVo.failure(ResultEnum.REQUEST_PARAM_LACK);
        }

        //获取缓存用户信息
        UserInfoVo users = (UserInfoVo) redisCache.getObj(KeyProfixConstant.TOKEN + qrCode);

        if (users == null) {
            return ResponseVo.failure("用户登录信息失效！");
        }

        //返回用户信息
        UserInfoVo userInfoVo = new UserInfoVo();
        BeanUtils.copyProperties(users, userInfoVo);
        userInfoVo.setToken(qrCode);

        return ResponseVo.success(userInfoVo);
    }

    /**
     * 根据ID获取用户信息 + {\"userId\":\"用户ID 必填 L\"}
     *
     * @return
     */
    @PostMapping("/info")
    public ResponseVo userInfo(@RequestBody JSONObject json,
                               @RequestHeader(name = StarterConstant.TOKEN) String token) {
        Long userId = json.getLong("userId");
        Assert.isTrue(userId != null, "请求参数缺失！");
        UserInfoVo userInfo = usersLogic.getUserByToken(token);
        if (userInfo == null) {
            throw new BrillilabException(ResultEnum.USER_NOT_LOGIN);
        }
        LabMember labMember = labMemberService.selectMember(userInfo.getLabId(), userId);
        Assert.isTrue(labMember != null, "无法查询其他实验室成员!");
        return ResponseVo.success(usersLogic.getUserById(userId));
    }

    /**
     * Token获取用户信息 +
     *
     * @return
     */
    @GetMapping("/info")
    public ResponseVo getUserByToken(Long userId, @RequestHeader(name = StarterConstant.TOKEN) String token) {
        Assert.isTrue(StringUtils.isNotBlank(token), "请求参数缺失！");
        if (userId != null) {
            UserInfoVo userInfo = usersLogic.getUserByToken(token);
            if (userInfo == null) {
                throw new BrillilabException(ResultEnum.USER_NOT_LOGIN);
            }
            LabMember labMember = labMemberService.selectMember(userInfo.getLabId(), userId);
            Assert.isTrue(labMember != null, "无法查询其他实验室成员!");
            return ResponseVo.success(usersLogic.getUserById(userId));
        } else {
            // 通过token获取用户
            UserInfoVo infoVo = usersLogic.getUserByToken(token);
            if (infoVo == null) {
                throw new BrillilabException(ResultEnum.USER_NOT_LOGIN);
            }
            UserInfoVo userInfo = usersLogic.getUserInfo(infoVo.getLabId(), infoVo.getId());
            if (userInfo == null) {
                userInfo = usersLogic.getUserById(infoVo.getId());
            }
            userInfo.setToken(token);
            userInfo.setLabNum(infoVo.getLabNum());
            usersLogic.resetUserCache(token, userInfo);
            return ResponseVo.success(userInfo);
        }
    }

    /**
     * 用户切换实验室
     * {\"labId\":\"要切换到的实验室ID 必填 L\"}
     */
    @AspectLog
    @PostMapping("/changeLab")
    public ResponseVo changeLab(@RequestBody JSONObject json, @RequestHeader(name = StarterConstant.TOKEN) String token) {

        Long labId = json.getLong("labId");

        if (StringUtils.isBlank(token)) {
            return ResponseVo.failure(ResultEnum.FAILURE, "token缺失！", null);
        }
        if (labId == null) {
            return ResponseVo.failure(ResultEnum.REQUEST_PARAM_LACK);
        }

        //切换实验室的业务
        UserInfoVo userInfo = usersLogic.changeLab(token, labId);

        return ResponseVo.success(userInfo);
    }

    /**
     * 用户登出
     * <p>
     * 根据Token删除用户信息
     */
    @PostMapping("/logout")
    public ResponseVo logout(@RequestHeader(name = StarterConstant.TOKEN) String token) {
        if (StringUtils.isBlank(token)) {
            return ResponseVo.failure(ResultEnum.FAILURE, "token缺失！", null);
        }

        usersLogic.userLogout(token);

        return ResponseVo.success();
    }

    /**
     * 用户注册发送短信验证码
     * {\"mobile\":\"手机号 必填 S\"}
     *
     * @return
     */
    /**
     * 用户注册发送短信验证码 {\"mobile\":\"手机号 必填 S\"}
     *
     * @return
     */
    @PostMapping("/rgSmsCode")
    public ResponseVo sendRegisterVerifyCode(@RequestBody JSONObject json, @RequestHeader(name = StarterConstant.TOKEN) String token) {

        Long labId = json.getLong("labId");
        String mobile = json.getString("mobile");

        if (!StringUtils.isBlank(token)) {
            UserInfoVo userInfoVo = usersLogic.getUserByToken(token);
            labId = userInfoVo.getLabId();
        }

        Assert.isTrue(StringUtils.isNotBlank(mobile), "请输入手机号！");
        Assert.isTrue(ValidatorUtil.isPhone(mobile), "请输入正确的手机号！");

        // 判断用户是否存在
        ResponseVo failure = usersLogic.getExistUserOrExistMemberResult(labId, mobile);
        if (failure != null)
            return failure;

        boolean b = messageLogic.sendVerifyCode(mobile);
        Assert.isTrue(b, "短信验证失败！");
        UserRegVo userVo = usersLogic.getUserRegVo(mobile);
        return ResponseVo.success(userVo, "验证码已发送,请注意查收");
    }

    /**
     * 新用户注册
     *
     * @param registerReq
     * @return
     */
    @AspectLog
    @PostMapping("/register")
    public ResponseVo register(@RequestBody UserRegisterReq registerReq) {

        Assert.notNull(registerReq, "请求参数缺失！");
        Assert.notNull(registerReq.getLabId(), "labId 缺失！");
        Assert.isTrue(StringUtils.isNotBlank(registerReq.getMobile()), "请输入手机号！");
        Assert.isTrue(StringUtils.isNotBlank(registerReq.getSmsCode()), "请输入验证码！");
        Assert.isTrue(ValidatorUtil.isPhone(registerReq.getMobile()), "请输入正确的手机号码！");
        Assert.isTrue(StringUtils.isNotBlank(registerReq.getUserName()), "请填写姓名！");
        Assert.isTrue(registerReq.getUserName().length() <= 20, "姓名长度不可超过20个字符！");
        Assert.isTrue(StringUtils.isNotBlank(registerReq.getPassword()), "请输入密码！");
        Assert.isTrue(ValidatorUtil.isPassword(registerReq.getPassword()), "密码为6-20个字符，由字母，数字组成！");
        Assert.isTrue(StringUtils.isNotBlank(registerReq.getCertificateNo()), "请填写证件号！");
        Assert.isTrue(ValidatorUtil.isCertificateNo(registerReq.getCertificateNo()), "证件号为6-20个字符，包括字母或数字！");
        Assert.isTrue(StringUtils.isNotBlank(registerReq.getDuty()), "请选择职称！");

        // 判断用户是否存在
        ResponseVo failure = usersLogic.getExistUserOrExistMemberResult(registerReq.getLabId(), registerReq.getMobile());
        if (failure != null) return failure;

        // 校验短信验证码
        if (!StringUtils.isBlank(registerReq.getSmsCode())) {
            boolean b = messageLogic.checkVerifyCode(registerReq.getMobile(), registerReq.getSmsCode());
            Assert.isTrue(b, "验证码错误！");
        }

        //注册逻辑
        Users result = usersLogic.userRegister(registerReq.getLabId(),
                registerReq.getMobile().trim(), registerReq.getPassword().trim(),
                registerReq.getUserName().trim(), registerReq.getDuty(), registerReq.getEntryDate(), registerReq.getCertificateNo());


        UserInfoVo userInfo = new UserInfoVo();
        BeanUtils.copyProperties(result, userInfo);
        return ResponseVo.success(userInfo);
    }


    /**
     * 修改用户安全信息身份验证
     *
     * @param uavReq
     * @return
     */
    @AspectLog
    @PostMapping("/identityValid")
    public ResponseVo identityValidation(@RequestBody UserAuthValidateReq uavReq, @RequestHeader(name = StarterConstant.TOKEN) String token) {

        //参数校验
        Assert.isTrue(!StringUtils.isBlank(token) && uavReq != null && uavReq.getType() != null && StringUtils.isNotBlank(uavReq.getCertificateNo()), "请求参数缺失！");

        UserInfoVo userInfo = usersLogic.getUserByToken(token);

        usersLogic.validateCertificateNoOrEmail(uavReq, userInfo);

        IdentityValidateResp ivr = usersLogic.doIdentityValidate(uavReq, userInfo);

        return ResponseVo.success(ivr);

    }

    /**
     * 修改用户安全信息
     *
     * @param umReq
     * @return
     */
    @AspectLog
    @PostMapping("/securityModify")
    public ResponseVo securityModify(@RequestBody UserModifyReq umReq, @RequestHeader(name = StarterConstant.TOKEN) String token) {

        //参数校验
        Assert.isTrue(StringUtils.isNotBlank(token) && umReq != null && umReq.getType() != null && StringUtils.isNotBlank(umReq.getValidateId()), "请求参数缺失！");
        UserInfoVo userInfo = usersLogic.getUserByToken(token);

        UserInfoVo userInfoVo = null;
        if (umReq.getType().equals(UserModifyReq.Type.MOBILE.getCode())) {
            userInfoVo = usersLogic.modifyMobile(userInfo, umReq.getMobile(), umReq.getValidateCode(), umReq.getValidateId());
        } else if (umReq.getType().equals(UserModifyReq.Type.EMAIL.getCode())) {
            userInfoVo = usersLogic.modifyEmail(userInfo, umReq.getEmail(), umReq.getValidateCode(), umReq.getValidateId());
        } else if (umReq.getType().equals(UserModifyReq.Type.PASSWORD.getCode())) {
            boolean rs = usersLogic.modifyPasswordWithValidate(userInfo.getId(), umReq.getPassword(), umReq.getValidateId());
            Assert.isTrue(rs, "密码修改失败！");
            userInfoVo = userInfo;
        } else if (umReq.getType().equals(UserModifyReq.Type.SECURITY_PWD.getCode())) {
            boolean rs = usersLogic.modifySecurityPwd(userInfo.getId(), umReq.getSecurityPwd(), umReq.getValidateId());
            Assert.isTrue(rs, "数据密码修改失败！");
            userInfoVo = userInfo;
        } else {
            return ResponseVo.failure(ResultEnum.FAILURE, "修改类型不存在！", null);
        }

        //更新缓存信息
        usersLogic.resetUserCache(token, userInfoVo);

        ResponseVo success = ResponseVo.success(userInfo);
        switch (umReq.getType()) {
            case 1:
                success.setMessage("手机号修改成功！");
                break;
            case 2:
                success.setMessage("邮箱修改成功！");
                break;
            case 3:
                success.setMessage("密码修改成功！");
                break;
            case 4:
                success.setMessage("数据密码修改成功！");
                break;
            default:
                break;
        }

        return success;
    }

    /**
     * 用户实验室列表
     *
     * @return
     */
    @RequestMapping(value = "/lab/list", method = RequestMethod.GET)
    public ResponseVo labList(@RequestHeader(name = StarterConstant.TOKEN) String token) {
        //获取token获取用户缓存信息
        if (StringUtils.isBlank(token)) {
            return ResponseVo.failure(ResultEnum.FAILURE, "token缺失！", null);
        }
        UserInfoVo userInfo = usersLogic.getUserByToken(token);

        if (userInfo == null) {
            return ResponseVo.failure(ResultEnum.FAILURE, "用户登录信息失效！", null);
        }

        List<LabInfoVo> labList = usersLogic.getLabList(userInfo.getId());

        return ResponseVo.success(labList);

    }


    /**
     * 忘记密码身份校验
     *
     * @return
     */
    @RequestMapping(value = "/forget/identityValid", method = RequestMethod.POST)
    public ResponseVo forgetIdentityValid(@RequestBody JSONObject jsonObject) {

        String mobile = jsonObject.getString("mobile");
        String validateCode = jsonObject.getString("validateCode");
        String certificateNo = jsonObject.getString("certificateNo");

        Assert.isTrue(!StringUtils.isBlank(mobile), "请输入手机号！");
        Assert.isTrue(!StringUtils.isBlank(validateCode), "请输入验证码！");
        Assert.isTrue(!StringUtils.isBlank(certificateNo), "请输入证件号或邮箱！");
        Assert.isTrue(ValidatorUtil.isPhone(mobile), "请输入正确的手机号！");

        Users user = usersLogic.getUser(mobile);
        Assert.isTrue(user != null, "该手机号未注册！");

        //证件号校验
        if (ValidatorUtil.isCertificateNo(certificateNo)) {
            Assert.isTrue(user.getCertificateNo().equals(certificateNo), "证件号或邮箱错误！");
        } else if (ValidatorUtil.isEmail(certificateNo)) {
            Assert.isTrue(certificateNo.equals(user.getEmail()), "证件号或邮箱错误！");
        } else {
            return ResponseVo.failure("证件号或邮箱错误！");
        }
        //手机验证码校验
        boolean b = usersLogic.validateIdentityByMobile(mobile.trim(), validateCode.trim());
        Assert.isTrue(b, "验证码错误！");

        //生成校验成功缓存信息
        String validateId = CodeUtil.getUUID();
        redisCache.set(KeyProfixConstant.VALIDATE_ID + validateId, user.getId(), 120);

        IdentityValidateResp ivr = new IdentityValidateResp();
        ivr.setUserId(user.getId());
        ivr.setValidateId(validateId);
        return ResponseVo.success(ivr);
    }

    /**
     * 忘记密码修改密码
     *
     * @param jsonObject
     * @return
     */
    @RequestMapping(value = "/forget/pswModify", method = RequestMethod.POST)
    public ResponseVo forgetModify(@RequestBody JSONObject jsonObject) {

        String password = jsonObject.getString("password");
        String validateId = jsonObject.getString("validateId");

        Assert.isTrue(!StringUtils.isBlank(password), "请输入新密码！");
        Assert.isTrue(ValidatorUtil.isPassword(password), "密码为6-20个字符，由字母，数字组成！");
        Assert.isTrue(!StringUtils.isBlank(validateId), "validateId 缺失！");

        boolean b = usersLogic.forgetPasswordModify(validateId, password);
        Assert.isTrue(b, "密码修改失败！");

        return ResponseVo.success("密码修改成功！");
    }


    /**
     * 修改个人信息
     *
     * @param users
     * @param token
     * @return
     */
    @AspectLog
    @RequestMapping(value = "/modify", method = RequestMethod.POST)
    public ResponseVo modify(@RequestBody Users users, @RequestHeader(name = StarterConstant.TOKEN) String token) {

        Assert.isTrue(!StringUtils.isBlank(token) && users != null, "请求参数缺失！");

        //设置值
        UserInfoVo userInfo = usersLogic.getUserByToken(token);

        Users me = usersLogic.getUser(userInfo.getId());

        if (!StringUtils.isBlank(users.getName())) {
            Assert.isTrue(users.getName().length() <= 20, "姓名长度不可超过20个字符！");
            me.setName(users.getName());
            userInfo.setName(users.getName());
        }

        if (!StringUtils.isBlank(users.getImageUrl())) {
            me.setImageUrl(users.getImageUrl());
            userInfo.setImageUrl(users.getImageUrl());
        }

        if (users.getSex() != null) {
            me.setSex(users.getSex());
            userInfo.setSex(users.getSex());
        }

        //更新
        boolean rs = usersService.updateById(me);
        Assert.isTrue(rs, "用户信息修改失败！");

        //重设缓存信息
        usersLogic.resetUserCache(token, userInfo);

        return ResponseVo.success(userInfo);
    }

    /**
     * 添加新成员审核
     *
     * @param userInviteApplyInfoVo
     * @param token
     * @return
     */
    @AspectLog
    @PostMapping("/addLabMemberAudit")
    public ResponseVo addLabMemberAudit(@RequestBody UserInviteApplyInfoVo userInviteApplyInfoVo, @RequestHeader(name = StarterConstant.TOKEN) String token) {

        Users user = usersLogic.getUser(userInviteApplyInfoVo.getMobile());
        Assert.isTrue(StringUtils.isNotEmpty(userInviteApplyInfoVo.getMobile()), "手机号不能为空！");
        Assert.isTrue(ValidatorUtil.isPhone(userInviteApplyInfoVo.getMobile()), "手机号格式错误");
        Assert.isTrue(StringUtils.isNotEmpty(userInviteApplyInfoVo.getRealName()), "姓名不能为空！");
        Assert.isTrue(StringUtils.isNotBlank(userInviteApplyInfoVo.getSmsCode()), "短信验证码不能为空！");
        Assert.isTrue(userInviteApplyInfoVo.getDuty() != null, "职务参数不能为空！");
        Assert.isTrue(!userInviteApplyInfoVo.getDuty().equals(0), "职务参数有误！");
        if (user == null) {
            Assert.isTrue(StringUtils.isNotEmpty(userInviteApplyInfoVo.getPassword()), "密码不能为空！");
            Assert.isTrue(StringUtils.isNotEmpty(userInviteApplyInfoVo.getSecurityPwd()), "数据密码不能为空！");
        }
        return usersLogic.validateMemberInfo(userInviteApplyInfoVo, user, token);
    }

    /**
     * 首次登录修改密码
     *
     * @param formInfo
     * @return
     */
    @PostMapping("/modifyPwd")
    public ResponseVo modifyPwd(@RequestBody UserModifyPwdVo formInfo) {
        Assert.isTrue(!StringUtils.isBlank(formInfo.getToken()), "请输入用户token！");
        Assert.notNull(StringUtils.isNotBlank(formInfo.getVerifyCode()),"请输入短信验证码！");
        Assert.isTrue(!StringUtils.isBlank(formInfo.getSecurityPwd()), "请输入数据密码！");
        Assert.isTrue(!StringUtils.isBlank(formInfo.getPassword()), "请输入登录密码！");
        Assert.isTrue(ValidatorUtil.isPassword(formInfo.getPassword()), "密码为6-20个字符，由字母，数字组成！");
        Assert.isTrue(!StringUtils.isBlank(formInfo.getSecurityPwd()), "请输入数据密码！");

        UserInfoVo userInfoVo = usersLogic.getUserByToken(formInfo.getToken());
        Assert.isTrue(userInfoVo.getIsOwner(), "非PI用户禁止调用此接口！");
        Users user = usersLogic.getUser(userInfoVo.getId());
        Assert.isTrue(user.getIsChangedPwd() == 0, "初始密码已修改过");

        boolean b = usersLogic.modifyPwdInfo(userInfoVo, formInfo);
        return ResponseVo.success(b);

    }

}
